package com.hyl.types.sdk.weixin;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.security.AnyTypePermission;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.util.*;

public class XmlUtil {

    // IMPORTANT: Make XStream a static final field for performance and consistent configuration
    // This ensures it's initialized only once and has all necessary configurations.
    private static final XStream xstream;

    static {
        // Initialize XStream here once
        xstream = new XStream(new DomDriver()); // Use DomDriver for both if you don't need PrettyPrintWriter for deserialization
        // Or, if you prefer the XppDriver for both, use new XppDriver()
        // Allow all types for simplicity in this example, but in production, narrow it down
        xstream.addPermission(AnyTypePermission.ANY);

        // Process annotations for all classes that might be deserialized/serialized
        // For your case, it's primarily MessageTextEntity
        xstream.processAnnotations(MessageTextEntity.class);

        // Optional: Ignore unknown elements to prevent errors if WeChat sends new fields
        xstream.ignoreUnknownElements();

        // Set mode if needed, NO_REFERENCES is good for simple beans
        xstream.setMode(XStream.NO_REFERENCES);
    }


    /**
     * 解析微信发来的请求(xml) - This method uses Dom4j, not XStream, so it's fine.
     */
    @SuppressWarnings("unchecked")
    public static Map<String, String> xmlToMap(HttpServletRequest request) throws Exception {
        // From request gets the input stream
        try (InputStream inputStream = request.getInputStream()) {
            // Stores parsing results in HashMap
            Map<String, String> map = new HashMap<>();
            // Reads input stream
            SAXReader reader = new SAXReader();
            // Gets XML document
            Document document = reader.read(inputStream);
            // Gets XML root element
            Element root = document.getRootElement();
            // Gets all child nodes of the root element
            List<Element> elementList = root.elements();
            // Iterates through all child nodes
            for (Element e : elementList)
                map.put(e.getName(), e.getText());
            // Releases resources
            inputStream.close();
            return map;
        }
    }

    /**
     * 将map转化成xml响应给微信服务器
     */
    static String mapToXML(Map map) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        mapToXML2(map, sb);
        sb.append("</xml>");
        try {
            return sb.toString();
        } catch (Exception e) {
            return null;
        }
    }

    private static void mapToXML2(Map map, StringBuffer sb) {
        Set set = map.keySet();
        for (Object o : set) {
            String key = (String) o;
            Object value = map.get(key);
            if (null == value)
                value = "";
            if (value.getClass().getName().equals("java.util.ArrayList")) {
                ArrayList list = (ArrayList) map.get(key);
                sb.append("<").append(key).append(">");
                for (Object o1 : list) {
                    HashMap hm = (HashMap) o1;
                    mapToXML2(hm, sb);
                }
                sb.append("</").append(key).append(">");

            } else {
                if (value instanceof HashMap) {
                    sb.append("<").append(key).append(">");
                    mapToXML2((HashMap) value, sb);
                    sb.append("</").append(key).append(">");
                } else {
                    sb.append("<").append(key).append("><![CDATA[").append(value).append("]]></").append(key).append(">");
                }

            }

        }
    }

    // Removed getMyXStream() as it's not needed if XStream is static and configured once.
    // If you need the PrettyPrintWriter customization for serialization, you can adapt the static block for it.
    // For now, let's keep it simple and consistent for both.

    /**
     * bean转成微信的xml消息格式
     * Now uses the static XStream instance
     */
    public static String beanToXml(Object object) {
        // No need to alias "xml" every time, it's done once in the static block via processAnnotations if @XStreamAlias("xml") is on MessageTextEntity.
        // If your root element is truly just "xml" for all objects and MessageTextEntity doesn't have @XStreamAlias("xml"),
        // then this alias("xml", object.getClass()) might be needed here, but generally, it's handled by annotations.
        // Assuming MessageTextEntity has @XStreamAlias("xml"), then this line is redundant here.
        // If not, you might need: xstream.alias("xml", object.getClass());
        // For MessageTextEntity, the @XStreamAlias("xml") is already there, so it's good.

        return xstream.toXML(object);
    }

    /**
     * xml转成bean泛型方法
     * Now uses the static XStream instance
     */
    public static <T> T xmlToBean(String resultXml, Class<T> clazz) {
        // The configurations are done in the static block.
        // No need to create a new XStream instance or reconfigure it every time.
        // Just call fromXML.
        return (T) xstream.fromXML(resultXml);
    }
}